using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace KaroGame.FrontEnd
{
    public class KaroTile : Microsoft.Xna.Framework.DrawableGameComponent
    {
        #region Properties
        KaroGame baseGame;
        private int id;
        private Model tileModel;
        private Matrix translationToPosition;
        private float x;
        private float y;
        private Vector3 defaultDiffuseColor;
        private KaroBoundingBox boundingBox;

        private bool active;
        private bool selected;
        private bool ghost;

        public int Id
        {
            get { return id; }
        }

        public float X
        {
            get { return x; }
        }

        public float Y
        {
            get { return y; }
        }

        public bool Active
        {
            get { return active; }
            set { active = value; }
        }

        public bool Selected
        {
            get { return selected; }
            set { selected = value; }
        }

        public bool Ghost
        {
            get { return ghost; }
            set { ghost = value; }
        }

        public Matrix TranslationToPosition
        {
            get { return translationToPosition; }
        }
        #endregion

        #region Constructors
        public KaroTile(Game game, Model tileModel, int id, float x, float y)
            : base(game)
        {
            this.id = id;
            baseGame = game as KaroGame;
            this.tileModel = tileModel;
            this.x = x;
            this.y = y;
            this.boundingBox = new KaroBoundingBox((BoundingBox)tileModel.Tag, ((KaroGame)Game).BasicEffect, ((KaroGame)Game).GraphicsDevice);
            this.defaultDiffuseColor = ((BasicEffect)tileModel.Meshes[0].Effects[0]).DiffuseColor;
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            Move(x, y);

            base.Initialize();
        }
        #endregion

        #region Public Methods
        public void Move(float newX, float newY)
        {
            this.x = newX;
            this.y = newY;

            translationToPosition = Matrix.CreateTranslation(new Vector3(x, y, 0));
        }

        public float? Intersects(Ray mouseRay)
        {
            Matrix transformation = GetTransformation();

            return boundingBox.Intersects(mouseRay, transformation);
        }
        #endregion

        #region Private Methods
        private Matrix GetTransformation()
        {
            return translationToPosition * ((KaroGame)Game).GetWorldMatrix;
        }
        #endregion

        #region UpdateDraw Cycle
        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
        }

        /// <summary>
        /// Allows the game component to draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            if (ghost && active)
            {
                baseGame.GraphicsDevice.RenderState.AlphaBlendEnable = true;
                baseGame.GraphicsDevice.RenderState.SourceBlend = Blend.One;
                baseGame.GraphicsDevice.RenderState.DestinationBlend = Blend.One;
            }
            else if (ghost && !active)
                return;
            else
                baseGame.GraphicsDevice.RenderState.AlphaBlendEnable = false;

            foreach (ModelMesh tileMesh in tileModel.Meshes)
            {
                foreach (BasicEffect tileEffect in tileMesh.Effects)
                {
                    tileEffect.World = GetTransformation();
                    tileEffect.View = ((KaroGame)Game).BasicEffect.View;
                    tileEffect.Projection = ((KaroGame)Game).BasicEffect.Projection;

                    Vector3 lightColor = Color.TransparentWhite.ToVector3();
                    if (active)
                    {
                        lightColor = new Vector3(1.5f, 1.5f, 1.5f);
                    }

                    if (selected)
                    {
                        tileEffect.DiffuseColor = Color.Yellow.ToVector3();
                    }

                    else
                    {
                        tileEffect.DiffuseColor = defaultDiffuseColor;
                    }

                    tileEffect.LightingEnabled = true;
                    tileEffect.DirectionalLight0.Enabled = true;
                    tileEffect.DirectionalLight0.DiffuseColor = lightColor;
                    tileEffect.DirectionalLight0.SpecularColor = Color.White.ToVector3();
                    tileEffect.DirectionalLight0.Direction = new Vector3(1, 0, -1);
                    tileEffect.DirectionalLight1.Enabled = true;
                    tileEffect.DirectionalLight1.DiffuseColor = lightColor;
                    tileEffect.DirectionalLight1.SpecularColor = Color.White.ToVector3();
                    tileEffect.DirectionalLight1.Direction = new Vector3(-1, 0, 1);
                }

                tileMesh.Draw();
            }
            baseGame.GraphicsDevice.RenderState.AlphaBlendEnable = false;

            // draw tile bounding box if asked so
            if (DebugController.IsDebugMode)
            {
                Matrix transformation = GetTransformation();
                boundingBox.Draw(transformation);
            }

            base.Draw(gameTime);
        }

        public void Activate()
        {
            active = true;
            boundingBox.Activate();
        }

        public void Deactivate()
        {
            active = false;
            boundingBox.Deactivate();
        }

        public void Select()
        {
            selected = true;
        }

        public void Deselect()
        {
            selected = false;
        }

        public Vector2 GetLeftTileCoordinates()
        {
            return new Vector2(x-1, y);
        }

        public Vector2 GetRightTileCoordinates()
        {
            return new Vector2(x+1, y);
        }

        public Vector2 GetTopTileCoordinates()
        {
            return new Vector2(x, y+1);
        }

        public Vector2 GetBottomTileCoordinates()
        {
            return new Vector2(x, y-1);
        }
        #endregion
    }
}